package com.example.util;

import cn.hutool.core.lang.Assert;

import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * 扩展lambda表达式中Optional功能
 * 原来判断 value！=null 改造为判断条件动态传入
 * @param <T>
 */
public final class Optionals<T> {

    private T value;

    private final Predicate<T> predicate;

    private Optionals(Predicate<T> predicate) {
        this.value = null;
        this.predicate = predicate;
    }

    private Optionals(T value, Predicate<T> predicate) {
        this.value = Objects.requireNonNull(value);
        this.predicate = predicate;
    }

    public static <T> Optionals<T> ofNullable(T value,Predicate<T> predicate) {
        Assert.isTrue(Objects.nonNull(predicate),()->new IllegalArgumentException("predicate cannot be null"));
        return new Optionals<>(value,predicate);
    }

    public T get() {
        Assert.isTrue(isPresent(),()->new IllegalArgumentException("No value present"));
        return value;
    }

    public boolean isPresent() {
        return predicate.test(value);
    }

    public void ifPresent(Consumer<? super T> consumer) {
        if (isPresent()){
            consumer.accept(value);
        }
    }

    public Optionals<T> map(Function<? super T, ? extends T> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return new Optionals<>(predicate);
        else {
            this.value = mapper.apply(value);
            return this;
        }
    }

    public <R> Optionals<R> map(Function<? super T, ? extends R> mapper,Predicate<R> predicate) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return new Optionals<>(predicate);
        else {
            return ofNullable(mapper.apply(value),predicate);
        }
    }

    public T orElse(T other) {
        return isPresent() ? value : other;
    }

    public T orElseGet(Supplier<? extends T> other) {
        return isPresent() ? value : other.get();
    }

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (isPresent()) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

}
